package com.zhengqing.demo.config.datasource.dynamic;


import cn.hutool.core.date.DateUtil;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.zhengqing.demo.config.TableNamesConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.core.rule.TableRule;
import org.apache.shardingsphere.shardingjdbc.jdbc.core.datasource.ShardingDataSource;
import org.apache.shardingsphere.underlying.common.rule.DataNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public class shardingConfig {

    @Autowired
    private DataSource dataSource;

    @Autowired
    private TableNamesConfig tableNamesConfig;

/*    @PostConstruct
    private  void intData() throws Exception{
        Date date =new Date();
        SimpleDateFormat format =  new SimpleDateFormat("yyyyMM");
        String s =  format.format(date);
        actualTablesRefresh(Integer.parseInt(s));
    }*/
    public void actualTablesRefresh()  throws Exception{
        DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) this.dataSource;
        Map<String,DataSource> map=dynamicRoutingDataSource.getCurrentDataSources();
        ShardingDataSource shardingDataSource=  (ShardingDataSource)map.get("sharding-data-source");

        if (tableNamesConfig.getNames() == null || tableNamesConfig.getNames().length == 0) {
            log.info("dynamic.table.names为空");
            return;
        }

        for(int i=0;i < tableNamesConfig.getNames().length; i++){
            TableRule tableRule=null;
            try {
                tableRule =  shardingDataSource.getRuntimeContext().getRule().getTableRule(tableNamesConfig.getNames()[i]);
            }catch (Exception e){
                log.info("获取分区表规则失败e:{}",e);
            }
            List<DataNode> dataNodes = tableRule.getActualDataNodes();
            Field actualDataNodesField = TableRule.class.getDeclaredField("actualDataNodes");
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(actualDataNodesField, actualDataNodesField.getModifiers() & ~Modifier.FINAL);
            List<DataNode> newDataNodes = new ArrayList<>();
            //格式化时间
            SimpleDateFormat format=new SimpleDateFormat("yyyyMM");
            //获取配置文件中默认时间的月份
            String defaultTime=tableNamesConfig.getStartYearMonth()+"";
            Date defaultDate=format.parse(defaultTime);
            Calendar calDate=Calendar.getInstance();
            calDate.setTime(defaultDate);
            //获取默认当前时间的月份
            Calendar cal = Calendar.getInstance();//使用默认时区和语言环境获得一个日历
            cal.add(Calendar.MONTH,1);
            //计算是否在两年内
            long betweenMonth=DateUtil.betweenMonth(defaultDate,cal.getTime(),true);
            String dataSourceName = dataNodes.get(0).getDataSourceName();
            if(betweenMonth<=12){
                while (true) {
                    DataNode dataNode = new DataNode(dataSourceName + "." + tableNamesConfig.getNames()[i]+format.format(calDate.getTime()));
                    newDataNodes.add(dataNode);
                    calDate.add(Calendar.MONTH,1);
                    betweenMonth--;
                    if(betweenMonth<0){
                        break;
                    }
                }
            }else{
                while (true){
                    calDate.add(Calendar.MONTH,1);
                    long cubetweenMonth=DateUtil.betweenMonth(calDate.getTime(),cal.getTime(),true);
                    if(cubetweenMonth<=12){
                        break;
                    }
                }
                while (true) {
                    DataNode dataNode = new DataNode(dataSourceName + "." + tableNamesConfig.getNames()[i] + format.format(calDate.getTime()));
                    newDataNodes.add(dataNode);
                    calDate.add(Calendar.MONTH, 1);
                    long cubetweenMonth=DateUtil.betweenMonth(calDate.getTime(),cal.getTime(),true);
                    if(cubetweenMonth==0){
                        break;
                    }
                }
            }


            actualDataNodesField.setAccessible(true);
            actualDataNodesField.set(tableRule, newDataNodes);

            Set<String> actualTables = Sets.newHashSet();
            Map<DataNode, Integer> dataNodeIntegerMap = Maps.newHashMap();
            AtomicInteger a = new AtomicInteger(0);
            newDataNodes.forEach((dataNode -> {
                actualTables.add(dataNode.getTableName());
                if (a.intValue() == 0) {
                    a.incrementAndGet();
                    dataNodeIntegerMap.put(dataNode, 0);
                } else {
                    dataNodeIntegerMap.put(dataNode, a.intValue());
                    a.incrementAndGet();
                }
            }));

            // 动态刷新：actualTables
            Field actualTablesField = TableRule.class.getDeclaredField("actualTables");
            actualTablesField.setAccessible(true);
            actualTablesField.set(tableRule, actualTables);
            // 动态刷新：dataNodeIndexMap
            Field dataNodeIndexMapField = TableRule.class.getDeclaredField("dataNodeIndexMap");
            dataNodeIndexMapField.setAccessible(true);
            dataNodeIndexMapField.set(tableRule, dataNodeIntegerMap);

            // 动态刷新：datasourceToTablesMap
            Map<String, Collection<String>> datasourceToTablesMap = Maps.newHashMap();
            datasourceToTablesMap.put(dataSourceName, actualTables);
            Field datasourceToTablesMapField = TableRule.class.getDeclaredField("datasourceToTablesMap");
            datasourceToTablesMapField.setAccessible(true);
            datasourceToTablesMapField.set(tableRule, datasourceToTablesMap);

        }

     }
    }

